<?php
    /*

     Version: MPL 1.1

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is KASSIOP Framework.

     The Initial Developer of the Original Code is SURIAN Nicolas (aka NairuS) <me@nairus.fr>.
     Portions created by the Initial Developer are Copyright (C) 2010
     The Initial Developer. All Rights Reserved.

     Contributor(s):

     Alternatively, the contents of this file may be used under the terms
     of the MPL license Version 1.1 (the  "MPL" License"), in which case the
     provisions of Version: MPL 1.1 License are applicable instead of those
     above.  If you wish to allow use of your version of this file only
     under the terms of the MPL License and not to allow others to use
     your version of this file under the MPL, indicate your decision by
     deleting  the provisions above and replace  them with the notice and
     other provisions required by the MPL License.  If you do not delete
     the provisions above, a recipient may use your version of this file
     under either the MPL License.

     The contents of this file are subject to the Mozilla Public License
     Version 1.1 (the "License"); you may not use this file except in
     compliance with the License. You may obtain a copy of the License at
     http://www.mozilla.org/MPL/

    */

    /**
     * The main class of all validators.
     *
     * @package    kassiop.system.validators
     * @subpackage base
     *
     * @author NairuS
     */
    abstract class BaseValidator
    {
        /**
         * Create an instance of BaseValidator
         *
         * Available options:
         *
         * required:    true if the value is required, false otherwise (default to true)
         * trim:        true if the value must be trimmed, false otherwise (default to true)
         * empty_value: empty value when value is not required
         *
         * Available error codes:
         *
         * required
         *
         * @param array $options   An array of options
         * @param array $messages  An array of error messages
         */
        public function __construct(array $options = array(), array $messages = array() )
        {
            // sets the default options and messages of the
            $defaultOptions = array
            (
                self::$emptyValueKey => null,
                self::$requiredKey   => true,
                self::$trimKey       => true
            ) ;

            $defaultMessages = array
            (
                self::$requiredKey => self::$requiredMessage,
                self::$invalidKey  => self::$invalidMessage
            ) ;

            $this->options  = array_merge( $defaultOptions , $this->options ) ;
            $this->messages = array_merge( $defaultMessages, $this->messages ) ;

            // launches the configuration of the current validator.
            $this->configure() ;

            // merges the options and messages passed-in the construtor.
            $this->options  = array_merge( $this->options , $options ) ;
            $this->messages = array_merge( $this->messages, $messages ) ;
        }

        /**
         * Defines the name of empty value option.
         *
         * @var string
         */
        public static $emptyValueKey = "emptyValue" ;

        /**
         * Defines the name of invalid option.
         *
         * @var string
         */
        public static $invalidKey = "invalid" ;

        /**
         * Defines the name of required option.
         *
         * @var string
         */
        public static $requiredKey = "required" ;

        /**
         * Defines the name of trim option.
         *
         * @var string
         */
        public static $trimKey = "trim" ;

        /**
         * Defines the default charset of the string to validate.
         *
         * @var string
         */
        protected static $charset = "UTF-8" ;

        /**
         * Defines the default invalid message to display.
         *
         * @var string
         */
        protected static $invalidMessage = "Invalid." ;

        /**
         * Defines the default required message to display.
         *
         * @var string
         */
        protected static $requiredMessage = "Required." ;

        /**
         * Defines the messages of the options.
         *
         * @var array
         */
        protected $messages = array() ;

        /**
         * Defines the options of the validator.
         *
         * @var array
         */
        protected $options = array() ;

        /**
         * Returns the charset to use when validating strings.
         *
         * @return string The charset (default to UTF-8)
         */
        public static function getCharset()
        {
            return self::$charset;
        }

        /**
         * Sets the charset to use when validating strings.
         *
         * @param string $charset  The charset
         */
        public static function setCharset( $charset )
        {
            self::$charset = $charset ;
        }

        /**
         * Cleans the input value.
         *
         * This method is also responsible for trimming the input value
         * and checking the required option.
         *
         * @param  mixed $value  The input value
         *
         * @return mixed The cleaned value
         *
         * @throws ErrorBaseValidator
         */
        public function clean( $value )
        {
            $clean = $value;

            if( $this->options[self::$trimKey] && is_string( $clean ) )
            {
              $clean = trim( $clean ) ;
            }

            // empty value?
            if( $this->isEmpty( $clean ) )
            {
              // required?
              if( $this->options[self::$requiredKey] )
              {
                throw new ErrorBaseValidator( $this, self::$requiredKey ) ;
              }

              return $this->getEmptyValue() ;
            }

            return $this->doClean( $clean ) ;
        }

        /**
         * Returns the value of the message name passed-in or null if not exists.
         *
         * @param string $name  The message name.
         * @return mixed        The message value or null if not exists.
         */
        public function getMessage( $name )
        {
            return $this->hasMessage( $name ) ? $this->messages[$name] : null ;
        }

        /**
         * Return all the messages configured.
         *
         * @return array
         */
        public function getMessages()
        {
            return $this->messages ;
        }

        /**
         * Returns the value of the option name passed-in or null if not exists.
         *
         * @param string $name  The option name.
         * @return mixed        The option value or null if not exists.
         */
        public function getOption( $name )
        {
            return $this->hasOption( $name ) ? $this->options[$name] : null ;
        }

        /**
         * Return all the options configured.
         *
         * @return array
         */
        public function getOptions()
        {
            return $this->options ;
        }

        /**
         * Returns true if the message name exists.
         *
         * @param string $name   The message name to check.
         * @return boolean
         */
        public function hasMessage( $name )
        {
            return isset( $this->messages[$name] ) ;
        }

        /**
         * Returns true if the option name exists.
         *
         * @param string $name   The option name to check.
         * @return boolean
         */
        public function hasOption( $name )
        {
            return isset( $this->options[$name] ) ;
        }

        /**
         * Tries to set an existant message.
         *
         * @param string $name   The message name
         * @param string $value  The message value
         * @return void
         */
        public function setMessage( $name, $value )
        {
            if( !in_array( $name, array_keys( $this->messages ) ) )
            {
                throw new InvalidArgumentException( sprintf( '%s does not support the following message: \'%s\'.', get_class( $this ), $name ) ) ;
            }
            $this->addMessage( $name, $value );
        }

        /**
         * Tries to set an existant option.
         *
         * @param string $name   The option name
         * @param mixed  $value  The option value
         * @return void
         */
        public function setOption( $name, $value )
        {
            if( !in_array( $name, array_keys( $this->options ) ) )
            {
                throw new InvalidArgumentException( sprintf( '%s does not support the following option: \'%s\'.', get_class( $this ), $name ) ) ;
            }
            $this->addOption( $name, $value );
        }

        /**
         * Adds an message.
         *
         * @param string $name   The message name.
         * @param string $value  The message value.
         * @return void
         */
        protected function addMessage( $name, $value )
        {
            $this->messages[$name] = $value ;
        }

        /**
         * Adds an option.
         *
         * @param string $name   The option name.
         * @param mixed  $value  The option value.
         * @return void
         */
        protected function addOption( $name, $value = null )
        {
            $this->options[$name] = $value ;
        }

        /**
         * Configures the current validator.
         *
         * This method overrided allows each validator to add options and error messages
         * during validator creation.
         *
         * If some options and messages are given in the ValidatorBase constructor
         * they will take precedence over the options and messages you configure
         * in this method.
         *
         * @return void
         *
         * @see __construct()
         */
        protected function configure()
        {
        }

        /**
         * Cleans the input value.
         *
         * Every subclass must implements this method.
         *
         * @param  mixed $value  The input value
         *
         * @return mixed The cleaned value
         *
         * @throws ErrorBaseValidator
         */
        protected function doClean( $value )
        {
        }

        /**
         * Returns true if the value is empty.
         *
         * @param  mixed $value  The input value
         *
         * @return bool true if the value is empty, false otherwise
         */
        protected function isEmpty( $value )
        {
            return in_array( $value, array( null, '', array() ), true ) ;
        }

        /**
         * Returns an empty value for this validator.
         *
         * @return mixed The empty value for this validator
         */
        protected function getEmptyValue()
        {
            return $this->getOption( self::$emptyValueKey ) ;
        }
    }
?>